package org.sakai.service;

import io.micrometer.common.util.StringUtils;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Item;
import jakarta.annotation.Resource;
import org.apache.ibatis.annotations.Mapper;
import org.sakai.dto.FileDTO;
import org.sakai.entity.*;
import org.sakai.entity.Result;
import org.sakai.mapper.MinioUserMapper;
import org.sakai.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
public class MinioService {
    @Resource(name = "AdminMinioClient")
    MinioClient AdminMinioClient;

    @Autowired
    MinioUserMapper minioUserMapper;

    /**
     * 获取用户bucket的最大值，逻辑上
     * @param userId 用户ID
     * @return 容量数字(MB)
     */
    public Integer getBucketSize(String userId){
        return minioUserMapper.selectByPrimaryKey(userId).getMaxsize();
    }


    /**
     * 列出指定MinIO存储桶中的目录内容。
     *
     * @param bucketName    MinIO存储桶名称，不能为空
     * @param directoryPath 要列出的目录路径，不能为空
     * @return 包含目录内容的FileDTO对象列表的结果
     * @throws Exception 如果列出目录时发生错误
     */
    public Result<List<FileDTO>> listDirectory(String bucketName, String directoryPath) throws Exception {
        if (StringUtils.isBlank(bucketName) || StringUtils.isBlank(directoryPath)) {
            throw new IllegalArgumentException("Bucket name and directory path must not be blank.");
        }

        // 防止路径穿越攻击
        String safeDirectoryPath = Paths.get(directoryPath).normalize().toString();

        List<FileDTO> objectInfos = new ArrayList<>();
        Iterable<io.minio.Result<Item>> results = AdminMinioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(safeDirectoryPath).build()
        );

        try {
            for (io.minio.Result<Item> result : results) {
                Item item = result.get();
                String formattedLastModified = item.lastModified().toInstant()
                        .atZone(ZoneId.systemDefault())
                        .format(DateTimeFormatter.ISO_DATE_TIME);

                FileDTO fileDTO = new FileDTO(item.objectName(),
                        formattedLastModified,
                        item.isDir(), item.size());
                objectInfos.add(fileDTO);
            }
        } catch (Exception e) {
            // 记录日志并转换异常
            e.printStackTrace();
            return Result.error(ErrorCode.LIST_DIRECTORY_FAILED);
        }

        return Result.success(objectInfos);
    }

    /**
     * 删除指定存储桶中的对象或目录。
     *
     * @param bucketName 存储桶名称
     * @param objectOrDirPath 要删除的对象或目录路径（如果是以 '/' 结尾的路径，则视为目录进行递归删除）
     * @return 返回操作结果，成功时返回Result.success(null)，失败时返回错误信息
     */
    public Result<Void> deleteObjectOrDirectory(String bucketName, String objectOrDirPath) {
        try {
            // 如果是目录路径（以 '/' 结尾），则递归删除目录下所有对象
            if (objectOrDirPath.endsWith("/")) {
                Iterable<io.minio.Result<Item>> results = AdminMinioClient.listObjects(
                        ListObjectsArgs.builder().bucket(bucketName).prefix(objectOrDirPath).build()
                );
                for (io.minio.Result<Item> result : results) {
                    Item item = result.get();
                    AdminMinioClient.removeObject(
                            RemoveObjectArgs.builder().bucket(bucketName).object(item.objectName()).build()
                    );
                }
            } else {
                // 否则删除单个对象
                AdminMinioClient.removeObject(
                        RemoveObjectArgs.builder().bucket(bucketName).object(objectOrDirPath).build()
                );
            }
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(ErrorCode.DELETE_OBJECT_FAILED);
        }
         return Result.success(null);
    }

    /**
     * 创建一个“文件夹”（通过上传一个空对象实现）
     *
     * @param bucketName 存储桶名称
     * @param folderPath 文件夹路径（以 '/' 结尾）
     * @return 操作结果
     */
    public Result<Void> createFolder(String bucketName, String folderPath) {
        // 确保路径以 '/' 结尾，表示为文件夹
        if (!folderPath.endsWith("/")) {
            folderPath += "/";
        }

        try {
            AdminMinioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(folderPath)
                            .stream(new ByteArrayInputStream(new byte[0]), 0, -1)
                            .build()
            );
            return Result.success(null);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(ErrorCode.CREATE_FOLDER_FAILED);
        }
    }

    /**
     * 上传一个文件流到指定路径
     *
     * @param bucketName 存储桶名称
     * @return 操作结果
     */
    public Result<Image> upload(String bucketName, String prefix, MultipartFile file) {
        try {
            String extensionName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
            String newFileName = UUID.randomUUID().toString() + "." + extensionName;
            String objectName = prefix + "/" + newFileName;
            InputStream inputStream = file.getInputStream();
            AdminMinioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, -1, 1024 * 1024 * 5) // 5MB 缓存上传
                            .build()
            );
            String base64Url = "data:" + file.getContentType() + ";base64," + java.util.Base64.getEncoder().encodeToString(file.getBytes());
            Image image = new Image(newFileName, base64Url);
            return Result.success(image);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(ErrorCode.UPLOAD_FILE_FAILED);
        }
    }

    /**
     * 上传多个文件（可模拟上传整个文件夹）
     *
     * @param bucketName 存储桶名称
     * @param prefix     对象名前缀（用于模拟文件夹结构）
     * @param files      多个文件流及其原始路径
     * @return 操作结果
     */
    public Result<Void> uploadFiles(String bucketName, String prefix, List<UploadFileRequest> files) {
        try {
            for (UploadFileRequest file : files) {
                String objectName = prefix + file.getFileName();
                ByteArrayInputStream stream = new ByteArrayInputStream(file.getContent());
                AdminMinioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .stream(stream, -1, 1024 * 1024 * 5)
                                .build()
                );
            }
            return Result.success(null);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(ErrorCode.UPLOAD_FILE_FAILED);
        }
    }

    /**
     * public
     * 根据文件对象名获取文件输入流
     *
     * @param objectName 文件对象名（即文件唯一标识符）
     * @return 返回文件的 InputStream 流
     */
    public InputStream getFileAsStream(String objectName) {
        try {
            // 获取文件信息并以输入流形式返回
            return AdminMinioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket("public")
                            .object("images/" + objectName)
                            .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据文件对象名获取文件输入流
     *
     * @param objectName 文件对象名（即文件唯一标识符）
     * @return 返回文件的 InputStream 流
     */
    public InputStream getFileAsStream(String userId, String objectName) {
        try {
            // 获取文件信息并以输入流形式返回
            return AdminMinioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket("big-data-health")
                            .object(userId + "/" + "images/" + objectName)
                            .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
